namespace SampleModel;

class Model extends DataObject
{
	protected splitSuffix;
	protected isLoaded ;
	protected static splittable = false;

	public function __construct(splitSuffix = "")
	{
		if self::isSplittable() && empty splitSuffix {
			throw new \Exception(get_called_class() . " is splittable. Split suffix is needed.");
		}

		let this->isLoaded = false;
		let this->splitSuffix = splitSuffix;
	}

	public static function create(array! data, string splitSuffix = "", bool! isLoaded = false)
	{
		var modelClass, instance, mapping, columns;
		let modelClass = get_called_class();
		let instance = <Model> self::instantiateModel(modelClass, splitSuffix);

		let mapping = {modelClass}::getMapping(splitSuffix);
		if !fetch columns, mapping["columns"] {
        	throw new \Exception("Must have columns first");
        }
		instance->initialize(data, array_keys(columns));
		let instance->isLoaded = isLoaded;
		return instance;
	}

	public function delete() -> int
	{
		var modelName, mapping, primaryKey, accessor, effectRowCount;

		let modelName = get_called_class();
		let mapping = {modelName}::getMapping(this->splitSuffix);

		if !fetch primaryKey, mapping["key"] {
			throw new \Exception("Must have key first");
		}

		let accessor = this->initAccessor();

		accessor->filter(primaryKey, this->{primaryKey});

		let effectRowCount = accessor->delete();
		return effectRowCount;
	}

	public function save() -> int | bool
	{
        if this->isLoaded {
            return this->update();
        }
        return this->insert();
	}

    public function insert() -> int
    {
        var modelName, mapping, primaryKey, accessor, property, columns, data;
        let data = this->_data;
        let modelName = get_called_class();
        let mapping = {modelName}::getMapping(this->splitSuffix);

        if !fetch primaryKey, mapping["key"] {
        	throw new \Exception("Must have key first");
        }

        unset(data[primaryKey]);

        if !fetch columns, mapping["columns"] {
            throw new \Exception("Must have columns first");
        }

        let accessor = this->initAccessor();
        for property, _ in columns {
            if isset data[property] {
                accessor->setField(property, data[property]);
            }
        }
        return accessor->insert();
    }

	public function update() -> int | bool
	{
		var modelName, mapping, primaryKey, accessor, property, dirty, columns, data;

		let modelName = get_called_class();
		let mapping = {modelName}::getMapping(this->splitSuffix);

		if !fetch primaryKey, mapping["key"] {
			throw new \Exception("Must have key first");
		}

		if !fetch columns, mapping["columns"] {
		    throw new \Exception("Must have columns first");
		}

		let accessor = this->initAccessor();

		accessor->filter(primaryKey, this->{primaryKey});

		let dirty = this->_dirty;
        let dirty = array_unique(dirty);
		if empty dirty {
		    return false;
		}

        let data = this->_data;
		for property in dirty {
		    if isset columns[property] && isset data[property] {
		        accessor->setField(property, data[property]);
		    }
		}

		return accessor->update();
	}

	private static function instantiateModel(string! modelClass, string splitSuffix = "") -> <Model>
	{
		var instance, reflection;
		if is_php_version("5.6") {
        	let reflection = new \ReflectionClass(modelClass),
        		instance = reflection->newInstance(splitSuffix);
        } else {
			let instance = create_instance_params(modelClass, [splitSuffix]);
        }
        return instance;
	}

    public static function dataAccessor(string splitSuffix = "")
    {
    	var accessor, modelName;
    	let modelName = get_called_class();
       	let	accessor = {modelName}::getAccessor();
    	return {accessor}::useModel(modelName, splitSuffix);
    }

    public static function getAccessor(){
    	return "\\SampleModel\\MysqlAccessor";
    }

    public function initAccessor(){
    	return self::dataAccessor(this->splitSuffix);
    }

	public static function isSplittable() -> bool
	{
		var className, splittable;
		let className = get_called_class();
        %{
            zephir_read_static_property(&splittable, Z_STRVAL_P(className), Z_STRLEN_P(className), SL("splittable") TSRMLS_CC);
        }%
        return splittable;
	}

    public static function getPdoConfig(<MysqlAccessor> dataAccessor) -> string
    {
    	var modelName, readOnly, forceMaster;

    	let modelName = get_called_class();
    	let readOnly = dataAccessor->readOnly;
    	let forceMaster = dataAccessor->forceMaster;

    	if !readOnly || forceMaster {
    		return {modelName}::getPdoMasterConfig();
    	}

		return {modelName}::getPdoSalveConfig();
    }

    public static function getPdoSalveConfig() -> string
    {
    	return "default";
    }

    public static function getPdoMasterConfig() -> string
    {
    	return "default";
    }

    public static function getMapping(string! splitSuffix = "")
    {
    	throw new \Exception("Method 'getMapping' must be override");
    }
}
